﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Net;
using System.Reactive;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using GalaSoft.MvvmLight;
using GdPicture;
using LprMiddleWare.DAL;
using LprMiddleWare.HangWang;
using System.Reactive.Linq;
using System.Linq;
using System;
using LprMiddleWare.Converters;
using LprWrapperSharp;
using TlvLib;
using ErrorEventArgs = LprMiddleWare.HangWang.ErrorEventArgs;
using TimeConverter = LprMiddleWare.DAL.TimeConverter;

namespace LprMiddleWare.ViewModels
{
    using Model;

    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private ObservableCollection<LogEntry> _logs;
        private LprRecognizer _recognizer;
        private GdPictureImaging _gdPictureImaging;
        private ConcurrentBag<System.Net.IPAddress> _hosts = new ConcurrentBag<IPAddress>();
        private TlvLib.TlvUnpacker _unpacker;

        private LicensePlate _currentPlate;
        public LicensePlate CurrentPlate
        {
            get { return _currentPlate; }
            set
            {
                _currentPlate = value;
                RaisePropertyChanged("CurrentPlate");
            }
        }


        private Visibility _lprVisibility;
        public Visibility LprVisibility
        {
            get { return _lprVisibility; }
            set
            {
                _lprVisibility = value;
                RaisePropertyChanged("LprVisibility");
            }
        }

        public ObservableCollection<LogEntry> Logs
        {
            get { return _logs ?? (_logs = new ObservableCollection<LogEntry>()); }
        }


        private BitmapImage _currentImage;
        public BitmapImage CurrentImage
        {
            get { return _currentImage; }
            set
            {
                _currentImage = value;
                RaisePropertyChanged("CurrentImage");
            }
        }


        private HanWangCapturer _capturer;
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            _lprVisibility = Visibility.Collapsed;

            //_unpacker = new TlvUnpacker("localhost", 10000);
            //_unpacker.RawPacketReceived += new RawPacketHandler(_unpacker_RawPacketReceived);
            //_unpacker.Start();

            if (IsInDesignMode)
            {
                LogMessage("test log");
                CurrentImage = new BitmapImage(new Uri(@"/Images/TestPlate.jpg", UriKind.Relative));

                CurrentPlate = new LicensePlate();
                CurrentPlate.LicenseNumber = "川ABCD1234";
                CurrentPlate.DeviceId = "十字路口";
                CurrentPlate.LanNo = 1;
                CurrentPlate.CaptureTime = DateTime.Now;

            }
            else
            {
                _gdPictureImaging = new GdPictureImaging();
                _gdPictureImaging.SetLicenseNumber("4118011852689055553472356");

                // Code runs "for real": Connect to service, etc...
                LogMessage("初始化车牌抓拍接收模块");
                _capturer = new HanWangCapturer();
                _capturer.DeviceId = "abc";
                _capturer.Init();
                _capturer.Start();
                LogMessage("初始化车牌抓拍接收模块成功");

                LogMessage("初始化车牌识别模块");
                _recognizer = new LprRecognizer();
                _recognizer.SetImageFormat(ImageFormat.BGR, false, true);
                LogMessage("初始化车牌识别模块成功");


                var deviceStatus = Observable.FromEventPattern<DeviceStatusEventArgs>(_capturer, "DeviceStatusChanged");
                deviceStatus.ObserveOnDispatcher().Subscribe(OnDeviceStatus);

                var images = Observable.FromEventPattern<NewImageEventArgs>(_capturer, "NewImageReceived");
                var lprs = images.Select(e =>
                                  {
                                      var buffer = e.EventArgs.BitmapDatas[0];
                                      var i = buffer.ToImage();
                                      var rec = new Rectangle(0, i.Height / 2, i.Width, i.Height / 2);
                                      var result = _recognizer.RecogImage((Bitmap)i, i.Width, i.Height, rec);
                                      var lpr = new LicensePlate()
                                                    {
                                                        JpegBuffers = e.EventArgs.BitmapDatas,
                                                        LicenseNumber = result.PlateNumber,
                                                        CaptureTime = e.EventArgs.CaptureTime,
                                                        DeviceId = e.EventArgs.DeviceId,
                                                        LanNo = e.EventArgs.LanNo,
                                                        CaptureReason = e.EventArgs.CaptureReason,
                                                        PlateType = result.PlateType,
                                                        Speed = e.EventArgs.Speed
                                                    };
                                      return lpr;
                                  });

                lprs.ObserveOnDispatcher().Subscribe(OnNewLpr);

                var error = Observable.FromEventPattern<ErrorEventArgs>(_capturer, "ErrorOccurred");
                error.ObserveOnDispatcher().Subscribe(e => OnError(e.EventArgs));
            }
        }

        void _unpacker_RawPacketReceived(object sender, RawPacketReceivedEventArgs args)
        {
            var parser = new TlvLib.PacketParsers.LicensePlateParser();
            var p = parser.Parse(args.Packet);

            if (p.Images.Length > 0)
            {

                var lpr = new LicensePlate()
                              {
                                  LicenseNumber = p.PlateNo,
                                  CaptureTime = p.Images[0].CaptureTime,
                                  JpegBuffers = new List<byte[]>(p.Images.Select(i => i.ImageBuffer)),
                                  DeviceId = p.MonitorId.ToString(),
                                  LanNo = p.RoadNo,
                              };
                Application.Current.Dispatcher.BeginInvoke(new Action(() => OnNewLpr(lpr)));
            }

            System.Diagnostics.Debug.WriteLine("type: {0}, payload len: {1}", args.Packet.PacketType, args.Packet.Payload.Length);
        }

        private void OnError(ErrorEventArgs eventArgs)
        {
            LogMessage("发生错误：" + eventArgs.Message);
        }

        private void LogMessage(string message)
        {
            var log = new LogEntry() { Message = message };
            Logs.Insert(0, log);
        }

        private void OnNewLpr(LicensePlate lpr)
        {
            CurrentPlate = lpr;
            CurrentImage = lpr.JpegBuffers[0].ToBitmapImage();

            lpr.DeviceId = 1002.ToString();
            try
            {
                SaveCaptureRecord(lpr);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void SaveCaptureRecord(LicensePlate licensePlate)
        {
            var lane = Repository.Instance.GetLane(licensePlate.DeviceId, licensePlate.LanNo.ToString());
            var laneDetail = Repository.Instance.GetTollGate(lane.LaneNo);

            //是否超速
            if (licensePlate.CaptureReason == CaptureReason.Normal)
            {
                if (laneDetail.XDSD != 0)
                {
                    if (licensePlate.Speed > laneDetail.XDSD)
                    {
                        licensePlate.CaptureReason = CaptureReason.OverSpeed;
                    }
                }
            }

            var record = new TogVehmon();

            //地点信息
            record.KKBH = lane.KaKouNo;
            record.KKMC = lane.KakouName;
            record.FXBH = lane.DirectionNo;
            record.FXMC = lane.DirectionName;
            record.CDBH = lane.LaneNo;
            record.CDMC = lane.LaneName;
            //事件
            record.WZYY = PunishReasonConverter.Convert(licensePlate.CaptureReason);
            record.CLSD = licensePlate.Speed;
            record.XS = laneDetail.XDSD;
            record.SFCS = licensePlate.CaptureReason == CaptureReason.OverSpeed ? "1" : "0";
            if (licensePlate.CaptureReason != CaptureReason.Normal)
            {
                record.WZBJ = '1';
            }
            //时间
            record.JGSK = licensePlate.CaptureTime;
            record.TJRQ = TimeConverter.ToTongJiRiQi(licensePlate.CaptureTime);
            //车牌信息
            record.HPHM = licensePlate.LicenseNumber;
            var id = PlateTypeConverter.Convert(licensePlate.PlateType);
            var lpr = Repository.Instance.GetLprType(id);
            record.HPZL = lpr.HPZLDM;
            record.HPZLMC = lpr.HPMC;
            //图片
            var savedImages = SaveImages(licensePlate, lane);
            if (savedImages.Count >= 1)
            {
                record.TXMC1 = Path.GetFileName(savedImages[0]);
            }
            if (savedImages.Count >= 2)
            {
                record.TXMC2 = Path.GetFileName(savedImages[1]);
            }
            if (savedImages.Count >= 3)
            {
                record.TXMC3 = Path.GetFileName(savedImages[2]);
            }

            record.Save();
        }


        //打字格式
        //日期： 时间： 地点：
        //设备编号：  方向：
        private IList<string> SaveImages(LicensePlate licensePlate, Lane captureAt)
        {
            var result = new List<string>();
            int count = 0;
            foreach (var bitmapData in licensePlate.JpegBuffers)
            {
                var img = Image.FromStream(new MemoryStream(bitmapData));
                var overlayImageId = DrawOverlayText(licensePlate, captureAt, img);
                var relative = GetRelativeImagePath(licensePlate.CaptureTime, captureAt, count++);
                var path = Path.Combine(Properties.Settings.Default.ImageSaveDir, relative);
                var dir = Path.GetDirectoryName(path);
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                _gdPictureImaging.SaveAsJPEG(overlayImageId, path);
                _gdPictureImaging.ReleaseGdPictureImage(overlayImageId);
                img.Dispose();
                result.Add(path);
            }

            return result;
        }

        private int DrawOverlayText(LicensePlate licensePlate, Lane captureAt, Image img)
        {
            var orgImageId = _gdPictureImaging.CreateGdPictureImageFromBitmap((Bitmap)img);
            var text = string.Format("时间：{0}        地点：{1}\n设备编号：{2}        方向：{3}",
                                     licensePlate.CaptureTime, captureAt.KakouName, licensePlate.DeviceId, captureAt.DirectionName);
            var txtHeight = _gdPictureImaging.GetTextHeight(orgImageId, text, "Tahoma", 16, GdPicture.FontStyle.FontStyleRegular);
            var overlayImageId = _gdPictureImaging.CreateNewGdPictureImage(img.Width, (int)(img.Height + txtHeight),
                                                                           img.PixelFormat,
                                                                           Color.Black);
            _gdPictureImaging.DrawText(overlayImageId, text, 0, 0, 16, GdPicture.FontStyle.FontStyleRegular,
                                       Color.Yellow, "Tahoma", false);
            _gdPictureImaging.DrawGdPictureImage(orgImageId, overlayImageId, 0, (int)txtHeight, img.Width,
                                                 img.Height, InterpolationMode.Default);
            _gdPictureImaging.ReleaseGdPictureImage(orgImageId);
            return overlayImageId;
        }

        private void OnDeviceStatus(EventPattern<DeviceStatusEventArgs> arg)
        {
            //if (arg.EventArgs.Status == DeviceStatus.Connected)
            //{
            //    if (!_hosts.Contains(arg.EventArgs.Ip))
            //    {
            //        _hosts.Add(arg.EventArgs.Ip);
            //    }
            //}
            //else if (arg.EventArgs.Status == DeviceStatus.DisConnected)
            //{
            //    IPAddress ip;
            //    _hosts.TryTake(out ip);
            //}

            var msg = string.Format("{0}:{1} 来自设备 {2}", (arg.EventArgs.Status == DeviceStatus.Connected ? "连接" : "断开"), arg.EventArgs.Ip, arg.EventArgs.DeviceId);
            var item = new LogEntry()
                           {
                               Message = msg
                           };
            Logs.Insert(0, item);
        }

        public override void Cleanup()
        {
            // Clean own resources if needed
            if (_capturer != null)
            {
                _capturer.Dispose();
            }
            base.Cleanup();
        }

        private string GetRelativeImagePath(DateTime captureTime, Lane captureFrom, int index)
        {
            return string.Format("{0:d4}/{1:d2}/{2:d2}/{3}-{4:d4}{5:d2}{6:d2}{7:d2}{8:d2}{9:d2}-{10}.jpg",
                                 captureTime.Year, captureTime.Month, captureTime.Day,
                                 captureFrom.LaneNo,
                                 captureTime.Year, captureTime.Month, captureTime.Day, captureTime.Hour,
                                 captureTime.Minute, captureTime.Second,
                                 index);
        }


    }
}